Getting started

Installation

i18next can be added to your project using npm or yarn:
# npm
$ npm install i18next --save
# yarn
$ yarn add i18next
The default export is UMD compatible (commonjs, requirejs, global).
In the /dist folder you may find additional builds for commonjs, es6 modules. Optimized to load i18next in webpack, rollup, ... or node.js. The correct entry points are already configured in the package.json so there should be no extra setup to get the best build option.

Load in Deno

i18next can be imported like this:
import i18next from 'https://deno.land/x/i18next/index.js'
// or import i18next from 'https://raw.githubusercontent.com/i18next/i18next/master/src/index.js'
// or import i18next from 'https://cdn.jsdelivr.net/gh/i18next/i18next/src/index.js'
In this tutorial blog post you can check out how this works.

Load from CDN

You can also directly add a script tag loading i18next from one of the CDNs providing it:
unpkg.com
esm or cjs:
Make sure to use a fixed version in production like https://unpkg.com/i18next@17.0.0/dist/umd/i18next.js as passing no version will redirect to latest version which might contain breaking changes in future.
cdnjs.com
jsdelivr.com

Important Caveat

Before we dive into the first sample, please note the following: By default, i18next uses a key-based notation to look up translations, which comes with the benefit of additional structure for your translation files.
The downside of this is that your keys must not be in natural language — the names of the keys are not used as fallback content and the key names must not include reserved characters : and . since those are used by i18next.
If you prefer using natural language in keys, please read the fallback guide.

Basic sample

Please be aware these samples are just showing basic usage of the core functionality. For production usage please consider using one of our framework integrations to get better and simpler integrations (Setting innerHTML is just done to show how it works).
import i18next from 'i18next';
i18next.init({
lng: 'en', // if you're using a language detector, do not define the lng option
debug: true,
resources: {
en: {
translation: {
"key": "hello world"
}
}
}
});
// initialized and ready to go!
// i18next is already initialized, because the translation resources where passed via init function
document.getElementById('output').innerHTML = i18next.t('key');
Or using callback init signature:
import i18next from 'i18next';
i18next.init({
lng: 'en', // if you're using a language detector, do not define the lng option
debug: true,
resources: {
en: {
translation: {
"key": "hello world"
}
}
}
}, function(err, t) {
// initialized and ready to go!
document.getElementById('output').innerHTML = i18next.t('key');
});
Or using Promises:
i18next.init({
lng: 'en', // if you're using a language detector, do not define the lng option
debug: true,
resources: {
en: {
translation: {
"key": "hello world"
}
}
}
}).then(function(t) {
// initialized and ready to go!
document.getElementById('output').innerHTML = i18next.t('key');
});
Or using async/await:
await i18next.init({
lng: 'en', // if you're using a language detector, do not define the lng option
debug: true,
resources: {
en: {
translation: {
"key": "hello world"
}
}
}
});
// initialized and ready to go!
document.getElementById('output').innerHTML = i18next.t('key');
if you are lazy loading the translation resources, you may need to wait for i18next to have finished to initialize.
As you might see, this basic sample provides only one language directly added on init… let's extend this to have buttons to change language from English to German:
Do you quickly want to translate your resources to other languages? Try: https://translate.i18next.com
This is a working sample showing translated text. To learn more, have a look at the following extended sample:

Extended sample

The extended sample adds the language detector for our browser and the http-backend to load translation files from this documentation's i18next-gitbook repo.
You should now have an idea about how to achieve the basic setup. It's time to learn more about:

Comparison to others

You now might expect we compare eg. react-i18next to react-intl but that won't happen ;). But we will list why you should trust i18next to be the best choice for internationalization.

Sustainability

i18next was created in late 2011. What does that mean? It's older than most of the libraries you will use nowadays, including your main frontend tech (react, vue, ...).
Believe it or not but by the time of writing, the v11.x.x can be dropped in as a replacement for v1 by just adding a minimal compatibility layer. It's important to us not breaking things just for the sake of using new fancy stuff (which does not mean we do not keep up with latest possibilities of development).

Maturity

Based on how long i18next already is available open source, there is no real i18n case that could not be solved with i18next.
i18next and its localization service https://locize.com are used by companies small to very large.

Extensibility

With v2 of i18next we completely rebuild i18next to be as extensible as possible. Doing this i18next can be used in any javascript (and a few non-javascript - .net, elm, iOS, android, ...) environment, with any UI framework, with any i18n format, ... the possibilities are endless.
Just have a look at what the community built around the i18next core:

Richness

The regular i18n frameworks work like this:
  1. 1.
    You pass in all translations and the used language
  2. 2.
    You call a function that returns the correct translation based on the translations you passed in and provided values for plural and interpolation.
What you don't get by others - but get with i18next
  • Splitting translations into multiple files. Only load translations needed.
  • There are plugins to detect languages for most environments (browser, native, server). This enables to set priority of where to detect and even enables to cache set languages over requests / visits.
  • There are endless plugins to load translation from server, filesystem, ... these backends also assert that loading gets retried on failure, or that a file does not get loaded twice and callbacks of success are only called once. Those backends can even provide an additional layer for local caching eg. in localStorage.
  • Options what to load and how to fallback depending on language.
  • Support for objects and arrays
  • Full control over management of the translations stored.
  • Rich system of events to react on changes important to your application.
  • Freedom of i18n formats - prefer ICU? Just use i18next-icu plugin.

But I heard

i18next is complicated

True, i18next's documentation is bigger than that of other i18n frameworks - but that's also a tribute to offering a lot more features that you would have to build yourself otherwise (language detection, translation loading, ...).
If you do not need that i18next is as simple as any other i18n framework:
import i18next from 'i18next';
i18next.init({
lng: 'de',
resources: {
de: {
translation: {
"hello world": "hallo Welt"
}
}
}
});
i18next.t('hello world'); // hallo Welt

i18next is bloated

Hm... ask this in one year again. When your i18n framework needs to catch up with the needs of production systems.
We work hard on keeping the code base clean and readable. We add new features which might help a lot of users and not based on we think it's easy to solve.

i18next is too big

Yes, a size of 33kb minified and 9kb gzipped is huge. But like you saw before there is a lot more inside than just the basic i18n functionality.
But you could make this smaller by precompiling the translations. We could bring down i18next to 4kb gzipped (good to advertise) but on the other hand your translation files would grow significantly.

We tell you

I18next can do more

Just two samples of extended functionality you can get:

a) Ever liked to dynamically fallback your error messages to a general info if no specific message is available?

translation.json
{
"error": {
"unspecific": "Something went wrong.",
"404": "The page was not found."
}
}
Sample
const error = '404';
i18next.t([`error.${error}`, 'error.unspecific']) // -> "The page was not found"​
const error = '502';
i18next.t([`error.${error}`, 'error.unspecific']) // -> "Something went wrong"

b) Or like to say something like interval plurals:

  • many goodies still available
  • just a few goodies remaining...
  • no goodies remaining...sorry you're too late
Just drop in the interval-postprocessor
{
key_interval: '(0){no goodies remaining...sorry you`re too late};(1-100){just a few goodies remaining...};(100-inf){many goodies still available};'
}

Works on serverside

While some other i18n frameworks run on serverside too there are not many optimized for it. Loading translations only once (think of a render to string in react.js where you create a new instance and inject translations over and over). Also does it keep the set language during async requests or do simultaneous requests create race conditions in setting the right language?

Learn once - translate everywhere

Overview of what technologies i18next will integrate with. Some listed are: Electron, Phaser, iOS, Android, Aurelia, Meteor, React Native, Knockout, React, Next.js, Remix, Angular, Ember, jQuery, Vue, Grunt, IntelliJ IDEA, Webpack, NodeJS, Express, Django, Koa, Microsoft .NET, PHP, Ruby Rails, Couchbase, MongoDB, Redis, Firebase, Locize.
Should speak for itself.

We bridge the gap to localization

Being honest, internationalization is the smaller pain in getting a site translated. Localization and the translation process are where the real hard work starts.
With https://locize.com we fill this gap and enable a localization workflow as never seen before:

API

init

i18next.init(options, callback) // -> returns a Promise
The default export of the i18next module is an i18next instance ready to be initialized by calling init. You can create additional instances using the createInstance function.
Please read the options page for details on configuration options.
The callback will be called after all translations were loaded or with an error when failed (in case of using a backend).
So you should wait for init to complete (wait for the callback or promise resolution) before using the t function!
In case of react-i18next make sure useSuspense is enabled or handle the ready state in HOCs or hooks yourself.
Do not call init multiple times. To change language use changeLanguage. If you need complete different configs use createInstance or cloneInstance.
An error can occur if for example there was a loading issue when using a backend plugin.
i18next.init({
fallbackLng: 'en',
ns: ['file1', 'file2'],
defaultNS: 'file1',
debug: true
}, (err, t) => {
if (err) return console.log('something went wrong loading', err);
t('key'); // -> same as i18next.t
});
// with only callback
i18next.init((err, t) => {
if (err) return console.log('something went wrong loading', err);
t('key'); // -> same as i18next.t
});
// using Promises
i18next
.init({ /* options */ })
.then(function(t) { t('key'); });

use

i18next.use(module)
The use function is there to load additional plugins to i18next.
For available module see the plugins page and don't forget to read the documentation of the plugin.
import i18next from 'i18next';
import Backend from 'i18next-http-backend';
import Cache from 'i18next-localstorage-cache';
import postProcessor from 'i18next-sprintf-postprocessor';
import LanguageDetector from 'i18next-browser-languagedetector';
i18next
.use(Backend)
.use(Cache)
.use(LanguageDetector)
.use(postProcessor)
.init(options, callback);

t

i18next.t(keys, options)
Please have a look at the translation functions like interpolation, formatting and plurals for more details on using it.
You can specify either one key as a String or multiple keys as an Array of String. The first one that resolves will be returned.
i18next.t('my.key'); // -> will return value in set language
i18next.t(['unknown.key', 'my.key']); // -> will return value for 'my.key' in set language

exists

i18next.exists(key, options)
Uses the same resolve functionality as the t function and returns true if a key exists.
i18next.exists('my.key'); // -> true if exists, false if not

getFixedT

i18next.getFixedT(lng, ns, keyPrefix)
Returns a t function that defaults to given language or namespace.
All arguments can be optional/null.
lng and ns params could be arrays of languages or namespaces and will be treated as fallbacks in that case.
The optional keyPrefix will be automatically applied to the returned t function. i.e.
const t = i18next.getFixedT(null, null, 'user.accountSettings.changePassword')
const title = t('title'); // same as i18next.t('user.accountSettings.changePassword.title');
Do not use the keyPrefix option if you want to use keys with prefixed namespace notation:
i.e.
const t = i18next.getFixedT(null, null, 'user.accountSettings.changePassword')
const title = t('ns:title'); // this will not work
On the returned function you can like in the t function override the languages or namespaces by passing them in options or by prepending namespace.
// fix language to german
const de = i18next.getFixedT('de');
de('myKey');
// or fix the namespace to anotherNamespace
const anotherNamespace = i18next.getFixedT(null, 'anotherNamespace');
anotherNamespace('anotherNamespaceKey'); // no need to prefix ns i18n.t('anotherNamespace:anotherNamespaceKey');

changeLanguage

i18next.changeLanguage(lng, callback) // -> returns a Promise
Changes the language. The callback will be called as soon translations were loaded or an error occurs while loading.
Calling changeLanguage without lng uses the language detector to choose the language to set.
HINT: For easy testing—setting lng to 'cimode' will cause the t function to always return the key.
i18next.changeLanguage('en', (err, t) => {
if (err) return console.log('something went wrong loading', err);
t('key'); // -> same as i18next.t
});
// using Promises
i18next
.changeLanguage('en')
.then((t) => {
t('key'); // -> same as i18next.t
});
// manually re-detecting language
i18next.changeLanguage().then(...)

language

i18next.language
Is set to the current detected or set language.
If you need the primary used language depending on your configuration (supportedLngs, load) you will prefer using i18next.resolvedLanguage or i18next.languages[0].

languages

i18next.languages
Is set to an array of language codes that will be used to look up the translation value.
When the language is set, this array is populated with the new language codes. Unless overridden, this array is populated with less-specific versions of that code for fallback purposes, followed by the list of fallback languages.
Values are unique, so if they appear earlier in the array, they will not be added again.
// initialize with fallback languages
i18next.init({
fallbackLng: ["es", "fr", "en-US", "dev"]
});
// change the language
i18next.changeLanguage("en-US-xx");
// new language and its more generic forms, followed by fallbacks
i18next.languages; // ["en-US-xx", "en-US", "en", "es", "fr", "dev"]
// change the language again
i18next.changeLanguage("de-DE");
// previous language is not retained
i18next.languages; // ["de-DE", "de", "es", "fr", "en-US", "dev"]

resolvedLanguage

i18next.resolvedLanguage
Is set to the current resolved language. It can be used as primary used language, for example in a language switcher.
(introduced in v21.0.0)

loadNamespaces

i18next.loadNamespaces(ns, callback) // -> returns a Promise
Loads additional namespaces not defined in init options.
i18next.loadNamespaces('myNamespace', (err) => { /* resources have been loaded */ });
i18next.loadNamespaces(['myNamespace1', 'myNamespace2'], (err) => { /* resources have been loaded */ });
// using Promises
i18next
.loadNamespaces(['myNamespace1', 'myNamespace2'])
.then(() => {});

loadLanguages

i18next.loadLanguages(lngs, callback) // -> returns a Promise
Loads additional languages not defined in init options (preload).
i18next.loadLanguages('de', (err) => { /* resources have been loaded */ });
i18next.loadLanguages(['de', 'fr'], (err) => { /* resources have been loaded */ });
// using Promises
i18next
.loadLanguages(['de', 'fr'])
.then(() => {});

reloadResources

i18next.reloadResources() // -> returns a Promise
Reloads resources on given state. Optionally you can pass an array of languages and namespaces as params if you don't want to reload all.
// reload all
i18next.reloadResources();
// reload languages
i18next.reloadResources(['de', 'fr']);
// reload namespaces for all languages
i18next.reloadResources(null, ['ns1', 'ns2']);
// reload namespaces in languages
i18next.reloadResources(['de', 'fr'], ['ns1', 'ns2']);
// reload a namespace in a language
i18next.reloadResources('de', 'ns1');
// optional third param callback i18next@>=11.9.0
i18next.reloadResources('de', 'ns1', () => { /* reloaded */ });
// using Promises
i18next
.reloadResources()
.then(() => {});

setDefaultNamespace

i18next.setDefaultNamespace(ns)
Changes the default namespace.

dir

i18next.dir(lng)
Returns rtl or ltr depending on languages read direction.
// for current language
i18next.dir();
// for another language
i18next.dir('en-US'); // -> "ltr";
i18next.dir('ar'); // -> "rtl";

format

i18next.format(data, format, lng)
introduced in v8.4.0 and legacy since v21.3.0
Exposes interpolation.formatt function added on init.
For formatting used in translation files checkout the formatting doc.
// key = 'hello {{what}}'
i18next.t('key', { what: i18next.format('world', 'uppercase') }); // -> hello WORLD

instance creation

createInstance

i18next.createInstance(options, callback)
Will return a new i18next instance.
Please read the options page for details on configuration options.
Providing a callback will automatically call init.
The callback will be called after all translations were loaded or with an error when failed (in case of using a backend).
const newInstance = i18next.createInstance({
fallbackLng: 'en',
ns: ['file1', 'file2'],
defaultNS: 'file1',
debug: true
}, (err, t) => {
if (err) return console.log('something went wrong loading', err);
t('key'); // -> same as i18next.t
});
// is the same as
const newInstance = i18next.createInstance();
newInstance.init({
fallbackLng: 'en',
ns: ['file1', 'file2'],
defaultNS: 'file1',
debug: true
}, (err, t) => {
if (err) return console.log('something went wrong loading', err);
t('key'); // -> same as i18next.t
});

cloneInstance

i18next.cloneInstance(options)
Creates a clone of the current instance. Shares store, plugins and initial configuration. Can be used to create an instance sharing storage but being independent on set language or default namespaces.
const newInstance = i18next.cloneInstance({
fallbackLng: 'en',
defaultNS: 'file1'
});

events

Every event can be unsubscribed using
i18next.off('name', myFunction);
All attached listeners can be unsubscribed using
i18next.off('name');

onInitialized

i18next.on('initialized', function(options) {})
Gets fired after initialization.

onLanguageChanged

i18next.on('languageChanged', function(lng) {})
Gets fired when changeLanguage got called.

onLoaded

i18next.on('loaded', function(loaded) {})
Gets fired on loaded resources.

onFailedLoading

i18next.on('failedLoading', function(lng, ns, msg) {})
Gets fired if loading resources failed (after the in-built retry algorithm).

onMissingKey

i18next.on('missingKey', function(lngs, namespace, key, res) {})
Gets fired on accessing a key not existing. Needs saveMissing set to true.

store events

Please be aware the i18next.store is only available on i18next after the init call.

onAdded

i18next.store.on('added', function(lng, ns) {})
Gets fired when resources got added.

onRemoved

i18next.store.on('removed', function(lng, ns) {})
Gets fired when resources got removed.

resource handling

Can be accessed on i18next or i18next.services.resourceStore.

getResource

i18next.getResource(lng, ns, key, options)
Gets one value by given key.
options:
option
default
description
keySeparator
"."
char to separate keys, or false if no separator is preferred
ignoreJSONStructure
true
if a key is not found as nested key, it will try to lookup as flat key

addResource

i18next.addResource(lng, ns, key, value, options)
Adds one key/value.
options:
option
default
description
keySeparator
"."
char to separate keys, or false if no separator is preferred
silent
false
if set to true adding will not emit an added event

addResources

i18next.addResources(lng, ns, resources)
Adds multiple key/values.

addResourceBundle

i18next.addResourceBundle(lng, ns, resources, deep, overwrite)
Adds a complete bundle.
Setting deep (default false) param to true will extend existing translations in that file. Setting deep and overwrite (default false) to true it will overwrite existing translations in that file.
So omitting deep and overwrite will overwrite all existing translations with the one provided in resources. Using deep you can choose to keep existing nested translation and to overwrite those with the new ones.
i18next.addResourceBundle('en', 'translations', {
key: 'value',
}, true, true);

hasResourceBundle

i18next.hasResourceBundle(lng, ns)
Checks if a resource bundle exists.

getDataByLanguage

i18next.getDataByLanguage(lng)
Returns a resource data by language.

getResourceBundle

i18next.getResourceBundle(lng, ns)
Returns a resource bundle.

removeResourceBundle

i18next.removeResourceBundle(lng, ns)
Removes an existing bundle.

Configuration Options

i18next.init(options, callback)
All options for calling init() or createInstance().

Logging

option
default
description
debug
false
logs info level to console output. Helps finding issues with loading not working.

Languages, namespaces, resources

option
default
description
resources
undefined
resources to initialize with (if not using a backend plugin or not using addResourceBundle)
lng
undefined
language to use (overrides language detection). If set to 'cimode' the output text will be the key. Make sure you use the 'en-US' format, instead of underscores or similar.
fallbackLng
'dev'
language to use if translations in user language are not available. Setting it explicitly to false will not trigger to load the fallbackLng at all. See the Fallback docs.
supportedLngs
false
array of allowed languages
nonExplicitSupportedLngs
false
if true, will consider variants as supported when the main language is. E.g. en-US will be valid if en is in supportedLngs.
If true and using a backend like i18next-http-backend, this can cause some request errors.
load
'all'
strategy to define which language codes to lookup. Example: given set language is en-US: - 'all'['en-US', 'en', 'dev'] - 'currentOnly''en-US' - 'languageOnly''en'
preload
false
array of languages to preload. Important on server-side to assert translations are loaded before rendering views.
lowerCaseLng
false
locale will be fully lowercased; e.g. en-USen-us
cleanCode
false
main language will be lowercased; e.g. ENen, while leaving full locales like en-US
ns
'translation'
string or array of namespaces to load
defaultNS
'translation'
(if a ns option and no defaultNS option is defined, the first namespace is used as defaultNS option) (setting it to false or an empty array [] will disable this fallback behaviour)
default namespace used if not passed to the translation function
fallbackNS
false
string or array of namespaces to lookup key if not found in given namespace. See NS fallback docs.
partialBundledLanguages
false
allows some resources to be set on initialization while others can be loaded using a backend connector

Missing keys

option
default
description
saveMissing
false
calls save missing key function on backend if key not found
updateMissing
false
experimental: enable to update default values using the saveMissing (Works only if defaultValue is different from translated value. Only useful on initial development or when keeping code as source of truth not changing values outside of code. Only supported if backend supports it already)
saveMissingTo
'fallback'
'current' or 'all' By default it uses the configured fallback language to save the missing keys to. 'current' will use the current used/detected language (i18next.language) and 'all' will save it to all languages included in i18next.languages.
saveMissingPlurals
true
will save all plural forms instead of only singular if t was called for plurals
missingKeyHandler
false
function(lngs, ns, key, fallbackValue, updateMissing, options) { } used for custom missing key handling (needs saveMissing set to true!)
The options are an internal value container similar to the t() options.
parseMissingKeyHandler
noop
function(key, defaultValue) { // return value to display }
appendNamespaceToMissingKey
false
appends namespace to missing key
missingInterpolationHandler
noop
function(text, value) { return 'stringWithAlternativeValueOrUndefined' } gets called in case a interpolation value is undefined. This method will not be called if the value is an empty string or null
missingKeyNoValueFallbackToKey
false
Used to not fallback to the key as default value, when using saveMissing functionality. * i.e. when using with i18next-http-backend this will result in having a key with an empty string value.

Translation defaults

option
default
description
postProcess
false
string or array of postProcessors to apply per default
returnNull
true
allows null values as valid translation
returnEmptyString
true
allows empty string as valid translation
returnObjects
false
allows objects as valid translation result
returnDetails
false
returns an object that includes information about the used language, namespace, key and value
returnedObjectHandler
noop
function(key, value, options) {} gets called if object was passed in as key but returnObjects was set to false
joinArrays
false
char that arrays will be joined by; e.g. ", "
overloadTranslationOptionHandler
function(args) { return { defaultValue: args[1] }; };
default: sets defaultValue
interpolation
{...}
see interpolation
skipInterpolation
false
Allow translate function to skip interpolation and return raw values instead
simplifyPluralSuffix
(used in format < format v4)
true
will use 'plural' as suffix for languages only having 1 plural form, setting it to false will suffix all with numbers

Plugin options

option
default
description
detection
undefined
options for language detection - check docs
backend
undefined
options for backend - check docs
cache
undefined
options for a cache layer in backends - check docs

Others

option
default
description
initImmediate
true
triggers resource loading in init() inside a setTimeout (default async behaviour). Set it to false if your backend loads resources synchronously - that way, calling i18next.t() after init() is possible without relying on the initialization callback. This option only works for sync (blocking) loading backend, like i18next-fs-backend and i18next-sync-fs-backend!
keySeparator
'.'
char to separate keys. If working with a flat JSON, it's recommended to set this to false.
nsSeparator
':'
char to split namespace from key
pluralSeparator
'_'
char to split plural from key
contextSeparator
'_'
char to split context from key
appendNamespaceToCIMode
false
prefixes the namespace to the returned key when using lng: 'cimode'
ignoreJSONStructure
true
if a key is not found as nested key, it will try to lookup as flat key
maxParallelReads
10
limits parallel reads to the backend to prevent opening up to thousands of sockets or file descriptors at the same time, leading to EMFILE errors if ulimit -n is exceeded (debug: true must be set to see them). limiting parallelism usually makes loading all items substantially faster than allowing all reads to start before any have finished.

initImmediate

Sample using initImmediate when using a backend plugin allowing sync (blocking) loads.
This option only works for sync (blocking) loading backend, like i18next-fs-backend!
import i18next from 'i18next';
import Backend from 'i18next-fs-backend';
// not working
i18next
.use(Backend)
.init();
i18next.t('key'); // -> will not return value as init was run async
/*
execution order of function calls
- init
- t
- loadResources (as called inside timeout)
*/
// working
i18next
.use(Backend)
.init({ initImmediate: false });
i18next.t('key'); // -> will return value
/*
execution order of function calls
- init
- loadResources (as called without timeout)
- t
*/

Supported Frameworks

This list is not officially maintained; information here is contributed by the library maintainers themselves. Consult their GitHub page for details on issues and implementation. Lastly, some of those libraries might stop being updated without further notice, while others warn it in their pages. In the latter scenario, they're tagged as deprecated below.
Framework
Home
Extra details
Flutter
i18next
Also supports Flutter's LocalizationsDelegate
React
A powerful internationalization framework for React / React Native which is based on i18next.
Svelte
A Svelte wrapper for i18next
Next.js
next-i18next
The easiest way to translate your Next.js apps
Next.js
ni18n
An easy to use integration for Next.js to enable i18next translations on your application with support for SSR, SSG and Client translation loading.
Next.js
Flexible production-grade boilerplate with Next.js 9 and Zeit Now, with pre-configured Sentry, cookies, Amplitude, Emotion, FontAwesome, GraphQL/GraphCMS (Apollo), Bootstrap (Reactstrap), i18next (Locize), Jest, Cypress (E2E tests) and CI/CD (GH Actions), with full TypeScript support and support for B2B multi-tenants web apps (monorepo)
Remix
The easiest way to translate your Remix apps.
AngularJS
ng-i18next
Angular1/2 provider, directive and filter
Angular
ng2-i18next (actimeo)
Angular2 service and directive
Angular
angular-i18next (Romanchuk)
Angular 2.0+ integration (service, pipes, events)
Vue.js
i18next-vue
Vue 2 and Vue 3+ support
Vue.js
vue-i18next (rse)
Vue.js
vue-i18next2 (bluelovers)
Solid.js
Omi
omi-i18n
i18n solution for Omi
Inferno
(deprecated/gone) Translation utility for Inferno components
Elm
elm-i18next
Node/Deno HTTP server
Middleware to be used with Node.js web frameworks like Express or Fastify, and also for Deno.
Express
(deprecated) Middleware for the Express HTTP server/framework (Node)
Koa
Middleware for the Koa framework
Hapi
hapi-i18next
jQuery
Plugin to use i18next on jQuery selectors
HTML5
loc-i18next
Plugin to use the same API as jquery-i18next, but with HTML5 selectors
Aurelia
An Aurelia-Wrapper
Meteor
i18next repackaged for Meteor
Polymer
(deprecated) Polymer-friendly interface
Ember.js
Integrates i18next into Ember CLI apps
Ember.js
Ember CLI addon
Knockout.js
i18next-ko
KnockoutJS bindings
Phaser
Plugin for the HTML5 game framework
Construct 3
c3-i18next
Translation plugin for the Construct 3 game software
Metalsmith
(deprecated) Metalsmith plugin to easily create multiple localised branches of your site
.NET
I18Next.Net
Library based on .NET Standard 2.0 with a rich feature-set like the original i18next, supporting .NET DI and translations; also comes with plugin support
.NET
i18next-net
.NET C# class
PHP
Class for basic i18next functionality (2016). There's also a fork by Mika- (2017)
PHP
Class for basic i18next functionality (2019)
Rails
Asset gem containing bundled i18next JavaScript files
Rails
(deprecated) Asset pipeline localization using i18next for rails 3.2 & 4
Dart
i18next
An adaptation of i18next standard for Dart with support for Flutter localization techniques.
Elm
elm-i18next
Functions for working with dynamically loaded translations in Elm.
iOS
i18next-ios
Android
Web Components
kwc-i18next
Web component interfacing i18next
Web Components
i18next-wc
Web component interfacing i18next and Intl
Marko
Components for Marko templates
Virtual DOM
i18nextify
One-liner script to enable i18next on any site not using its own Virtual DOM
Handlebars
Helper that lets you translate inside your templates
Handlebars
Adds the features of i18next and Intl to Handlebars
lit-html/lit-element
lit-i18n
i18next translations using lit-html directives
Solid
Small library which covers i18next for Solid applications
Astro
An astro integration of i18next + some utility components to help you translate your astro websites!

Supported Environments

i18next supports the two most recent versions of evergreen browsers (Chrome, Firefox, Safari, etc). It also runs in Node and Deno.

Plugins and Utils

i18n formats

While the i18next format (JSON based) is the preferred solution and widely supported in translation management tools like locize.com, you might prefer another exciting format, like:
name
format
description
fluent
i18nFormat plugin to use mozilla fluent format with i18next
i18next-icu
ICU
i18nFormat plugin to use ICU format with i18next based on yahoo/intl-messageformat
polyglot
i18nFormat plugin to use airbnb/polyglot.js format with i18next

extraction tools

name
description
Scan your code, extract translation keys/values, and merge them into i18n resource files.
A simple command line and gulp plugin that lets you parse your code and extract the translations keys in it.
A babel plugin that can extract keys in JSONv4 format.
Nicely shows an overview of your translations in a UI. Check which keys are not yet translated.

utils

util
type
description
converter
Converts gettext .mo or .po to 18next json format and vice versa.
csv2i18next
converter
Convert CSV files to JSON & YAML for i18next.js
converter
Convert between CSV files and JSON format for i18next
resx2i18next
converter
Convert ResX-files to json resources compatible with i18next
extractor
Intellij IDEA Plugin for i18next resource generation
extractor
Extracts i18n keys and values from source files.
bundler
bundle language resource files for i18next
bundler
Convert gettext PO files into i18next JSON format during webpack builds
bundler
Load gettext PO files as i18next format directly in webpack
bundler
webpack loader that can translate your code and generate bundle per each language
bundler
This webpack loader generates the resources structure necessary for i18next. The structure is webpacked with the client bundle at build time, thus avoiding loading any language resources via extra HTTP requests.
bundler
Vite plugin to client bundle i18next locales composited from one to many json/yaml files from one to many libraries. Zero config HMR support included.
bundler
This loader generates the resStore config needed for i18next to avoid loading language resources via extra HTTP requests. It generates this config given a directory.
bundler
Yet another i18next webpack plugin. This plugin collects keys from webpack parsing phase, saves missing translations into specified path, copies translation files.
bundler/converter
Import .po files with rollup
util
analyse statically your code to find calls to i18next and validates them for all your supported languages
util
Keep i18next JSON resource files in sync with source language file
util
Syncs i18next locale resource files against a primary language. Supports namespaces, plural forms and key sorting.
util
Make sure that all text shown are translated
IDE integration
Navigation, code completion, highlighting
i18next-hmr
bundler / DX
HMR webpack plugin that allows to reload translation resources on client & server
util / DX
Nicely shows an overview of your translations in a UI. Check which keys are not yet translated.
converter
Convert old i18next translation resources to the new i18next v4 json format. Via CLI or programmatically.
online-converter
Convert old i18next translation resources to the new i18next v4 json format directly in your browser.
Intellij idea i18next support plugin.
bundler / DX
Webpack loader to fetch locale files with ES6 modules, supporting autocomplete, type checking, HMR and content based hashing

services

Services that are known to fully support the i18next format (plural handling, ...) and sponsoring or contributing to the development of i18next.
name
description
locize
localization as a service. Solves your localization process using i18next.

backend extenders

backend
description
combine multiple of the existing backends for fallback and caching scenarios
enable another backend's multiload behaviour of loading multiple lng-ns combinations with one request. This behaviour was removed from i18next >=v11.0.0 and could be enabled again by using this adapter

backends

backend
description
This is a i18next cache layer to be used in the browser. It will load and cache resources from localStorage and can be used in combination with the chained backend.
This is a i18next cache layer to be used in react native. It will load and cache resources from AsyncStorage and can be used in combination with the chained backend.
filesystem
backend layer for i18next used in Node.js and for Deno to load translations from the filesystem.
It can also be used as cache layer in combination with the chained backend, i.e. a chained backend together with the http backendor with the locize backend.
http backend
backend layer for i18next using in node.js, in the browser and for deno (will use xhr or fetch)
xhr backend
backend layer for i18next using browsers xhr deprecated
backend layer for i18next using browsers fetch
backend to load fluent syntax .ftl files via xhr
fetch missing keys on demand
backend layer for i18next used in Node.js & Deno to load translations from the MongoDB.
backend layer for i18next used in Node.js & Deno to load translations from Google's Firestore DB.
node.js backend layer for i18next using fs module to load resources from filesystem
node.js backend for i18next using fs module to load resources securely in an electron app from filesystem
node.js backend layer for i18next using request module to load resources from another server
i18next node.js backend layer for i18next using couchbase
i18next node.js backend layer for Zanata
i18next Backend Using Firebase
i18next backend for smart-bucket
Use webpack code splitting to load files as a javascript bundle
i18next backend for Transifex Native

language detector

language detector
description
Language detector that works universally (browser + server) - Meant to be used with a universal framework, such as Next.js
browser
language detector used in browser environment for i18next
http
language detector for "any" http backend, also for Deno
language detector for express.js (nodejs).
nodejs koa
A i18next language detecting plugin for Koa framework.
react native
language detector for React Native.
language detector for React Native that saves the user's choice in Async Storage, used for persistence.
electron
language detector for electron apps.
CLI
language detector for CLI.

post processors

post processor
description
sprintf post processor for i18next.
interval based plurals like. "One Item", "A few items", "A lot of items"
embed React elements inside your i18next translation strings
i18next post-processor for processing korean postposition
i18next post-processor for pseudolocalization of strings
i18next postProcessor plugin for Node.js and in the browser that replaces all words with emojis.

loggers

Only the integrated console logger is available for now.

Create your own plugin

Want to create your own plugins? Learn more here.

For Enterprises

Is i18n enough?

We believe not. The closer the release date of your product gets the more obvious it gets, that instrumenting your code for localization is not enough.
There are more points to address:
  • How does the translation process work?
  • How do the source files get to the translators and back?
  • How do you keep track which parts are already translated and which parts are not - and additional are all target languages fully translated?
  • How do you deploy new languages after release?
  • How do you handle versioning?
  • How do you update / fix typos in translations after deployment?
Translation Management Systems are a great help. But most tools out there are built for one-time translation of your documents. Not for continuously translating your application. So there is still a gap between the development and the translation process.
So is there a smart localization management platform? An i18n(ext) platform?

Locize

Locize is a localization as a service platform made by i18next. The close integration of i18next brings a lot of additional value and asserts you an additional level of support and saves you endless time spent on localization.
Locize brings you:
  • A beautiful editor to edit your translations
  • Enables a continuous localization process
  • An incontext editor to edit on your own website
  • Progress reporting
  • Usage reporting
  • Plural conversion between languages
  • Support for versions
  • Gets your missing keys passed directly to the project
  • Hosts translation on its CDN and allows for automatic or manual publishing (You still have the option to host translations yourself)
Check out how it works => https://youtu.be/ds-yEEYP1Ks

First setup help

Let's try to figure out what you need.

For which environment are you looking for an i18n solution?

There are a lot of appropriate libraries. Have a look at this list.

Special handling for serverless environments (AWS lambda, Google Cloud Functions, Azure Functions, etc...)

Make use of i18next-fs-backend
import i18next from 'i18next';
import Backend from 'i18next-fs-backend';
const backend = new Backend({
// path where resources get loaded from
loadPath: '/locales/{{lng}}/{{ns}}.json'
});
i18next
.use(backend)
.init({
// initImmediate: false, // setting initImediate to false, will load the resources synchronously
...opts,
...yourOptions
}); // yourOptions should not include backendOptions!
or just import/require your files directly
import i18next from 'i18next';
import en from './locales/en.json'
import de from './locales/de.json'
i18next
.init({
...opts,
...yourOptions,
resources: {
en,
de
}
});

Do you need a language detector for your environment?

Do you want to bundle the translations with your app?

Do you want to load the translations separate from your app via http?

Do you want to manage your translations with an awesome translation management system?


Essentials

Accessing keys

resources with 2 keys:
{
"key": "value of key",
"look": {
"deep": "value of look deep"
}
}
sample
i18next.t('key');
// -> "value of key"
i18next.t('look.deep');
// -> "value of look deep"

Passing a default value

You can pass in a default value for cases the key could not be found in translations like:
i18next.t('key', 'default value to show');
In case you're using the saveMissing functionality, it will also pass the defaultValue to your chosen backend, like shown in this React.js example.

Accessing keys in different namespaces

Namespaces are a feature in i18next internationalization framework which allows you to separate translations that get loaded into multiple files.
init
i18next.init({
ns: ['common', 'moduleA'],
defaultNS: 'moduleA'
});
moduleA.json
{
"name": "Module A"
}
common.json
{
"button": {
"save": "save"
}
}
sample
i18next.t('name');
// -> "Module A"
// from different namespace (not recommended with namespace prefix when used in combination with natural language keys)
i18next.t('common:button.save') // -> "save"
// better use the ns option:
i18next.t('button.save', { ns: 'common' }) // -> "save"

Multiple fallback keys

Calling the t function with an array of keys enables you to translate dynamic keys providing a non specific fallback value.
As a sample think of an error code you get and you like to show a specific warning in some cases:
keys
{
"error": {
"unspecific": "Something went wrong.",
"404": "The page was not found."
}
}
sample
// const error = '404';
i18next.t([`error.${error}`, 'error.unspecific']); // -> "The page was not found"
// const error = '502';
i18next.t([`error.${error}`, 'error.unspecific']); // -> "Something went wrong"

Overview options

i18next.t(key, options)
option
description
defaultValue
defaultValue to return if a translation was not found, you also can define defaults for plurals by adding defaultValue_other -> _suffix depends on same pluralrules.
count
count value used for plurals
context
used for contexts (eg. male / female)
replace
object with vars for interpolation - or put them directly in options
lng
override language to use
lngs
override languages to use
fallbackLng
override language to lookup key if not found see fallbacks for details
ns
override namespaces (string or array)
keySeparator
override char to separate keys
nsSeparator
override char to split namespace from key
returnObjects
accessing an object not a translation string (can be set globally too)
returnDetails
returns an object that includes information about the used language, namespace, key and value
joinArrays
char, eg. '\n' that arrays will be joined by (can be set globally too)
postProcess
string or array of postProcessors to apply see interval plurals as a sample
interpolation
override interpolation options
skipInterpolation
skip interpolation and nesting for this call to t function
ignoreJSONStructure
if a key is not found as nested key, it will try to lookup as flat key

Interpolation

Interpolation is one of the most used functionalities in I18N. It allows integrating dynamic values into your translations.
Per default, interpolation values get escaped to mitigate XSS attacks.
🎓 Check out this topic in the i18next crash course video.
If the interpolation functionality provided doesn't suit you, you can use i18next-sprintf-postProcessor for sprintf supported interpolation.

Basic

Interpolation is one of the most used functionalities in I18N.
Keys
Keys, by default, are strings surrounded by curly brackets:
{
"key": "{{what}} is {{how}}"
}
Sample
i18next.t('key', { what: 'i18next', how: 'great' });
// -> "i18next is great"

Working with data models

You can also pass entire data models as a value for interpolation.
Keys
{
"key": "I am {{author.name}}"
}
Sample
const author = {
name: 'Jan',
github: 'jamuhl'
};
i18next.t('key', { author });
// -> "I am Jan"

Unescape

By default, the values get escaped to mitigate XSS attacks. You can toggle escaping off, by either putting - before the key, or set the escapeValue option to false when requesting a translation.
Keys
{
"keyEscaped": "no danger {{myVar}}",
"keyUnescaped": "dangerous {{- myVar}}"
}
Sample
i18next.t('keyEscaped', { myVar: '<img />' });
// -> "no danger &lt;img &#x2F;&gt;"
i18next.t('keyUnescaped', { myVar: '<img />' });
// -> "dangerous <img />"
i18next.t('keyEscaped', { myVar: '<img />', interpolation: { escapeValue: false } });
// -> "no danger <img />" (obviously could be dangerous)
Warning: If you toggle escaping off, escape any user input yourself!

Additional options

Prefix/Suffix for interpolation and other options can be overridden in the init options or by passing additional options to the t function:
i18next.init({
interpolation: { ... }
});
i18next.t('key', {
interpolation: { ... }
});
option
default
description
escape
function
escape function function escape(str) { return str; }
escapeValue
true
escapes passed in values to avoid XSS injection
useRawValueToEscape
false
If true, then value passed into escape function is not casted to string, use with custom escape function that does its own type-checking
prefix
"{{"
prefix for interpolation
suffix
"}}"
suffix for interpolation
While there are a lot of options going with the defaults should get you covered.

All interpolation options

option
default
description
format
noop function
format function, read formatting for details
formatSeparator
","
used to separate format from interpolation value
escape
function
escape function function escape(str) { return str; }
escapeValue
true
escape passed in values to avoid XSS injection
useRawValueToEscape
false
If true, then value passed into escape function is not casted to string, use with custom escape function that does its own type-checking
prefix
"{{"
prefix for interpolation
suffix
"}}"
suffix for interpolation
prefixEscaped
undefined
escaped prefix for interpolation (regexSafe)
suffixEscaped
undefined
escaped suffix for interpolation (regexSafe)
unescapeSuffix
undefined
suffix to unescaped mode
unescapePrefix
"-"
prefix to unescaped mode
nestingPrefix
"$t("
prefix for nesting
nestingSuffix
")"
suffix for nesting
nestingPrefixEscaped
undefined
escaped prefix for nesting (regexSafe)
nestingSuffixEscaped
undefined
escaped suffix for nesting (regexSafe)
nestingOptionsSeparator
","
separates the options from nesting key
defaultVariables
undefined
global variables to use in interpolation replacements defaultVariables: { key: "value" }
maxReplaces
1000
after how many interpolation runs to break out before throwing a stack overflow
skipOnVariables
true
(was false for <v21.0.0)
Will skip to interpolate the variables, example:
t('key', { a: '$t(nested)' })
this will not resolve the nested key and will use$t(nested) as the variable value. Another example:
t('key', { a: '{{otherVar}}': otherVar: 'another value' })
this will not resolve the otherVar variable and will use{{otherVar}}as the variable value.
If your interpolation variables are user provided or loaded from an external source, we strongly suggest to keep this option to true.
If you know what you're doing, you can also set this to false.

Formatting

Starting with i18next>=21.3.0 you can use the built-in formatting functions based on the Intl API.
You may need to polyfill the Intl API:
🎓 Check out this topic in the i18next crash course video.

Basic usage

The translation string has the following signature:
{
"key": "Some format {{value, formatname}}",
"keyWithOptions": "Some format {{value, formatname(option1Name: option1Value; option2Name: option2Value)}}"
}

Passing options to the formatting:

  1. 1.
    In the translation string using {{value, formatname(options1: options1Value)}}
  2. 2.
    Using the root level options when calling t('key', { option1: option1Value })
  3. 3.
    Using the per value options like: t('key', { formatParams: { value: { option1: option1Value } })
Samples
// JSON
{
"intlNumber": "Some {{val, number}}",
"intlNumberWithOptions": "Some {{val, number(minimumFractionDigits: 2)}}"
}
i18next.t('intlNumber', { val: 1000 });
// --> Some 1,000
i18next.t('intlNumber', { val: 1000.1, minimumFractionDigits: 3 });
// --> Some 1,000.100
i18next.t('intlNumber', { val: 1000.1, formatParams: { val: { minimumFractionDigits: 3 } } });
// --> Some 1,000.100
i18next.t('intlNumberWithOptions', { val: 2000 });
// --> Some 2,000.00
i18next.t('intlNumberWithOptions', { val: 2000, minimumFractionDigits: 3 });
// --> Some 2,000.000

Overriding the language to use

The language can be overridden by passing it in t.options
i18next.t('intlNumber', { val: 1000.1, lng: 'de' }); // or: i18next.t('intlNumber', { val: 1000.1, locale: 'de' });
i18next.t('intlNumber', { val: 1000.1, formatParams: { val: { lng: 'de' } } }); // or: i18next.t('intlNumber', { val: 1000.1, formatParams: { val: { locale: 'de' } } });

Adding custom format function

It's rather simple to add own function:
// after i18next.init(options);
i18next.services.formatter.add('lowercase', (value, lng, options) => {
return value.toLowerCase();
});
i18next.services.formatter.add('underscore', (value, lng, options) => {
return value.replace(/\s+/g, '_');
});
Make sure you add your custom format function AFTER the i18next.init() call.
There's also an addCached version for optimized performance:
i18next.services.formatter.addCached('specialformat', (lng, options) => {
const formatter = new Intl.NumberFormat(lng, options);
return (val) => formatter.format(val);
});

Using multiple formatters

{
"key": "Some format {{value, formatter1, formatter2}}"
}

Built-in formats

Number

// JSON
{
"intlNumber": "Some {{val, number}}",
"intlNumberWithOptions": "Some {{val, number(minimumFractionDigits: 2)}}"
}
i18next.t('intlNumber', { val: 1000 });
// --> Some 1,000
i18next.t('intlNumber', { val: 1000.1, minimumFractionDigits: 3 });
// --> Some 1,000.100
i18next.t('intlNumber', { val: 1000.1, formatParams: { val: { minimumFractionDigits: 3 } } });
// --> Some 1,000.100
i18next.t('intlNumberWithOptions', { val: 2000 });
// --> Some 2,000.00
i18next.t('intlNumberWithOptions', { val: 2000, minimumFractionDigits: 3 });
// --> Some 2,000.000

Currency

// JSON
{
"intlCurrencyWithOptionsSimplified": "The value is {{val, currency(USD)}}",
"intlCurrencyWithOptions": "The value is {{val, currency(currency: USD)}}",
"twoIntlCurrencyWithUniqueFormatOptions": "The value is {{localValue, currency}} or {{altValue, currency}}",
}
i18next.t('intlCurrencyWithOptionsSimplified', { val: 2000 });
// --> The value is $2,000.00
i18next.t('intlCurrencyWithOptions', { val: 2300 });
// --> The value is $2,300.00
i18next.t('twoIntlCurrencyWithUniqueFormatOptions',
{
localValue: 12345.67,
altValue: 16543.21,
formatParams: {
localValue: { currency: 'USD', locale: 'en-US' },
altValue: { currency: 'CAD', locale: 'fr-CA' },
},
},);
// --> The value is $12,345.67 or 16 543,21 $ CA

DateTime

// JSON
{
"intlDateTime": "On the {{val, datetime}}",
}
i18next.t('intlDateTime', { val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) });
// --> On the 12/20/2012
i18next.t('intlDateTime',
{
val: new Date(Date.UTC(2012, 11, 20, 3, 0, 0)),
formatParams: {
val: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' },
},
});
// --> On the Thursday, December 20, 2012

RelativeTime

// JSON
{
"intlRelativeTime": "Lorem {{val, relativetime}}",
"intlRelativeTimeWithOptions": "Lorem {{val, relativetime(quarter)}}",
"intlRelativeTimeWithOptionsExplicit": "Lorem {{val, relativetime(range: quarter; style: narrow;)}}",
}
i18next.t('intlRelativeTime', { val: 3 });
// --> Lorem in 3 days
i18next.t('intlRelativeTimeWithOptions', { val: -3 });
// --> Lorem 3 quarters ago
i18next.t('intlRelativeTimeWithOptionsExplicit', { val: -3 });
// --> Lorem 3 qtrs. ago
i18next.t('intlRelativeTimeWithOptionsExplicit', { val: -3, style: 'long' });
// --> Lorem 3 quarters ago

List

// JSON
{
"intlList": "A list of {{val, list}}"
}
i18next.t('intlList', { val: ['locize', 'i18next', 'awesomeness'] });
// --> A list of locize, i18next, and awesomeness

Legacy format function i18next<21.3.0

You can add formatting using moment.js and numeral.js or the intl api.
As a sample using momentjs to format dates.
keys
{
"key": "The current date is {{date, MM/DD/YYYY}}",
"key2": "{{text, uppercase}} just uppercased"
}
Init i18next with a format function:
i18next.init({
interpolation: {
format: function(value, format, lng) {
if (format === 'uppercase') return value.toUpperCase();
if(value instanceof Date) return moment(value).format(format);
return value;
}
}
});
sample
i18next.t('key', { date: new Date() });
// -> "The current date is 07/13/2016"
i18next.t('key2', { text: 'can you hear me' });
// => "CAN YOU HEAR ME just uppercased"
Keep the language on moment in sync with i18next by listening to the change language event:
i18next.on('languageChanged', function(lng) {
moment.locale(lng);
});

Additional options

Prefix/Suffix for interpolation and other options can be overridden in init option:
sample
i18next.init({
interpolation: { ... }
});
option
default
description
alwaysFormat
false
used to always call the format function for all interpolated values
format
noop function
Passing this function is considered LEGACY in i18next>=21.3.0
format function function format(value, format, lng, edit) {}
formatSeparator
','
used to separate format from interpolation value
While there are a lot of options going with the defaults should get you covered.

Plurals

Plural can be combined with interpolation, context, ...
These plurals are streamlined with the one used in the Intl API. You may need to polyfill the Intl.PluralRules API, in case it is not available it will fallback to the i18next JSON format v3 plural handling.
Note: The variable name must be count. And it must be present: i18next.t('key', {count: 1}); There will be no fallback to the 'key' value if count is not provided.
If you need multiple counts, take a look at nesting
We provide the ability to have special translation for {count: 0}, so that a more natural language can be used. If the count is 0, and a _zero entry is present, then it will be used instead of the language plural suffix.
🎓 Check out this topic in the i18next crash course video.

Singular / Plural

keys
{
"key_one": "item",
"key_other": "items",
"keyWithCount_one": "{{count}} item",
"keyWithCount_other": "{{count}} items"
}
sample
i18next.t('key', {count: 0}); // -> "items"
i18next.t('key', {count: 1}); // -> "item"
i18next.t('key', {count: 5}); // -> "items"
i18next.t('key', {count: 100}); // -> "items"
i18next.t('keyWithCount', {count: 0}); // -> "0 items"
i18next.t('keyWithCount', {count: 1}); // -> "1 item"
i18next.t('keyWithCount', {count: 5}); // -> "5 items"
i18next.t('keyWithCount', {count: 100}); // -> "100 items"
With v21.0.0 a new JSON format v4 was introduced that changed the suffixes. To convert your existing translations to the new v4 format, have a look at i18next-v4-format-converter or this web tool.

Languages with multiple plurals

Sample uses arabic which has 5 plural forms beside the singular.
keys
{
"key_zero": "zero",
"key_one": "singular",
"key_two": "two",
"key_few": "few",
"key_many": "many",
"key_other": "other"
}
sample
i18next.t('key', {count: 0}); // -> "zero"
i18next.t('key', {count: 1}); // -> "singular"
i18next.t('key', {count: 2}); // -> "two"
i18next.t('key', {count: 3}); // -> "few"
i18next.t('key', {count: 4}); // -> "few"
i18next.t('key', {count: 5}); // -> "few"
i18next.t('key', {count: 11}); // -> "many"
i18next.t('key', {count: 99}); // -> "many"
i18next.t('key', {count: 100}); // -> "other"

How to find the correct plural suffix?

You can use this small utility to get the correct plural suffixes.
Or try translation-check, it shows an overview of your translations in a nice UI. It shows also the appropriate plural forms.

Or you use a smart translation management system, like locize

Ordinal plurals

There is also support for ordinal numbers (referring to the ordering or ranking of things, e.g. "1st", "2nd", "3rd" in English). The ordinal option tells the helper to use the ordinal digit to determine the plurality key used. E.g., for "32" the ordinal digit is "2" so key_two is used.
keys
// i.e. english
{
"key_one": "{{count}}st place", // 1st, 21st, 31st
"key_two": "{{count}}nd place", // 2nd, 22nd, 32nd
"key_few": "{{count}}rd place", // 3rd, 23rd, 33rd
"key_other": "{{count}}th place" // 4th, 5th, 24th, 11th
}
sample
i18next.t('key', { count: 1, ordinal: true }); // -> "1st place"
i18next.t('key', { count: 21, ordinal: true }); // -> "21st place"
i18next.t('key', { count: 2, ordinal: true }); // -> "2nd place"
i18next.t('key', { count: 11, ordinal: true }); // -> "11th place"
i18next.t('key', { count: 32, ordinal: true }); // -> "32nd place"

Interval plurals

Want to define phrases expressing the number of items lies in a range. Like a few items or a lot of items.
You will need to add a post processor: i18next-intervalplural-postprocessor
import i18next from 'i18next';
import intervalPlural from 'i18next-intervalplural-postprocessor';
i18next
.use(intervalPlural)
.init(i18nextOptions);
keys
{
"key1_one": "{{count}} item",
"key1_other": "{{count}} items",
"key1_interval": "(1)[one item];(2-7)[a few items];(7-inf)[a lot of items];",
"key2_one": "{{count}} item",
"key2_other": "{{count}} items",
"key2_interval": "(1)[one item];(2-7)[a few items];"
}
sample
i18next.t('key1_interval', {postProcess: 'interval', count: 1}); // -> "one item"
i18next.t('key1_interval', {postProcess: 'interval', count: 4}); // -> "a few items"
i18next.t('key1_interval', {postProcess: 'interval', count: 100}); // -> "a lot of items"
// not matching into a range it will fallback to
// the regular plural form
i18next.t('key2_interval', {postProcess: 'interval', count: 1}); // -> "one item"
i18next.t('key2_interval', {postProcess: 'interval', count: 4}); // -> "a few items"
i18next.t('key2_interval', {postProcess: 'interval', count: 100}); // -> "100 items"
Note: The regex for the interval entry has changed in v3.0.0 of i18next-intervalPlural-postProcessor so if you are using the older versions, you need to use the curly braces instead of the bracketes, e.g.:
"key2_interval": "(1){one item};(2-7){a few items};"

Nesting

Nesting allows you to reference other keys in a translation. Could be useful to build glossary terms.

Basic

keys
{
"nesting1": "1 $t(nesting2)",
"nesting2": "2 $t(nesting3)",
"nesting3": "3",
}
sample
i18next.t('nesting1'); // -> "1 2 3"
You can reference keys from other namespaces by prepending the namespace: "nesting1": "1 $t(common:nesting2)",

Passing options to nestings

You can pass entire data models in options.
keys
{
"girlsAndBoys": "They have $t(girls, {\"count\": {{girls}} }) and $t(boys, {\"count\": {{boys}} })",
"boys": "{{count}} boy",
"boys_other": "{{count}} boys",
"girls": "{{count}} girl",
"girls_other": "{{count}} girls",
}
sample
i18next.t('girlsAndBoys', {girls: 3, boys: 2});
// -> "They have 3 girls and 2 boys"
Make sure the options string is valid JSON and can be parsed using JSON.parse
'sampleKey': 'test $t(nest2, { "changedVarName": "{{var}}" })'

Passing nesting to interpolated

keys
{
"key1": "hello world",
"key2": "say: {{val}}"
}
sample
i18next.t('key2', {val: '$t(key1)'});
// -> "say: hello world"
If you're using >= v21.0.0 you need to set skipOnVariables to false:
interpolation: {
skipOnVariables: false
}

Additional options

Prefix/Suffix for nesting and other options can be overridden in init interpolation options or by passing additional options to t function:
sample
i18next.init({
interpolation: { ... }
});
i18next.t('key', {
interpolation: { ... }
});
option
default
description
nestingPrefixEscaped
undefined
escaped prefix for nesting (regexSafe)
nestingSuffixEscaped
undefined
escaped suffix for nesting (regexSafe)
While there are a lot of options going with the defaults should get you covered.

Context

By providing a context you can differ translations. Eg. useful to provide gender specific translations.
🎓 Check out this topic in the i18next crash course video.

Basic

keys
{
"friend": "A friend",
"friend_male": "A boyfriend",
"friend_female": "A girlfriend"
}
sample
i18next.t('friend'); // -> "A friend"
i18next.t('friend', { context: 'male' }); // -> "A boyfriend"
i18next.t('friend', { context: 'female' }); // -> "A girlfriend"

Combining with plurals

You can pass entire data models in options.
keys
{
"friend_male_one": "A boyfriend",
"friend_female_one": "A girlfriend",
"friend_male_other": "{{count}} boyfriends",
"friend_female_other": "{{count}} girlfriends"
}
sample
i18next.t('friend', {context: 'male', count: 1}); // -> "A boyfriend"
i18next.t('friend', {context: 'female', count: 1}); // -> "A girlfriend"
i18next.t('friend', {context: 'male', count: 100}); // -> "100 boyfriends"
i18next.t('friend', {context: 'female', count: 100}); // -> "100 girlfriends"

Objects and Arrays

Objects

You can return objects or arrays to be used by third party modules localization:
keys
{
"tree": {
"res": "added {{something}}"
},
"array": ['a', 'b', 'c']
}
sample
i18next.t('tree', { returnObjects: true, something: 'gold' });
// -> { res: 'added gold' }
i18next.t('array', { returnObjects: true });
// -> ['a', 'b', 'c']
The returned value supports interpolation, plurals, nesting, ...
returnObjects can be set to true on init.

Arrays

You can access array values or join them.
keys
{
"arrayJoin": [
"line1",
"line2",
"line3"
],
"arrayJoinWithInterpolation": [
"you",
"can",
"{{myVar}}"
],
"arrayOfObjects": [
{ "name": "tom" },
{ "name": "steve" }
]
}
sample
i18next.t('arrayJoin', { joinArrays: '+' });
// -> "line1+line2+line3"
i18next.t('arrayJoinWithInterpolation', { myVar: 'interpolate', joinArrays: ' ' });
// -> "you can interpolate"
i18next.t('arrayOfObjects.0.name');
// -> "tom"
The returned value supports interpolation, plurals, nesting, ...
joinArrays can be set to a value on init.

Translation Resolution

The process of translating keys is the heart of i18next, and as such this document should serve as a guide to the overall process by which i18next attempts to translate your keys into the appropriate content for a given location, be it on a specific page and/or for a user in a particular region of the world.

Core Elements

Keys

A key is not unlike a key in any object structure, like JSON or a dictionary in Python. A key is a specific set of text that, when looked up, provides a corresponding value.

Example:

"key": "value"
This example shows the very core concept of what a key is capable of expressing, but the ability to express this formally is very important, as it allows us to expand its utility going forward.
Keys are a very powerful way of specifying the different forms of an element, be it a piece of text or other forms of content, into its potential variations.
For more information on all of the different ways keys can be used, please see the documentation for the translation function.

Languages

A language is what you would expect: the idiom to be used for translating a key. When we look for a key, we specify a language with it, so that we know which version of the key to use. The important thing to note about this is that if a key is not found, you can gracefully fall back to a parent language or a default one.
In i18next, a language is a particular value which can be known as a "code". A language code can be expressed in variety of ways, but they generally look something like the following example:

Example:

"en-US"
Long story short, you'll either use a "pure language" code, such as en or de for English and German, or a language + a variant identification, such as pt-BR for the Brazilian Portuguese, es-419 for Latin American Spanish, or zh-cmn-Hant-HK which is Chinese in the Mandarin variation, written in the Traditional script, as used in Hong Kong. For more information on the subject, it is recommended to read up on IETF Language Codes.

Namespaces

A namespace can be thought of as logical groupings of different sets of translations.
For instance, you might have a 3 sections of your app, each with many individual pages in them, but only 2 sections share similar content. If that's the case, instead of loading all of the keys for all 3 sections, you can instead load keys from a "shared" set of translations and break up the other sections into much smaller sets of keys, loading them as needed.
In a given namespace you could have a set of languages, each with its own set of keys.

Example

"common" // Things that are reused everywhere, like "Confirm" and "Cancel" on buttons
"validation" // All validation text, like "email address not valid" in a form
"glossary" // Words we want to be reused consistently, like key words in your app
For more information on the concept of namespaces and how you might want to use them, please see their documentation.

Resolution Order

By default, when translating a key, i18next tries the first combination of your namespace, language, and key.
However, if that does not work, i18next attempts to gracefully fallback to a different combination in order to provide the most relevant translation for a piece of content. The core idea is to try to find a key that exists, from most specific to least specific. Here is the process that it uses by default:

1. Similar Keys

If the specific key is not found, i18next tries to match the key you are looking for with a similar key, looking for a key that best fits the plural form, context, and singular form in that order.

2. Languages

If a key is not found, i18next then walks through the list of languages, which consists of the current language(s) and the fallback language(s).

3. Namespaces

If no language matches, i18next walks through the list of namespaces, which similarly to languages, consists of the current namespace(s) and the fallback namespace(s).

4. Fallback Keys

If that key is still not found, i18n will walk through this process with the fallback key(s), if specified.

5. Key Not Found

If the key is still not found, i18n will then return the key itself, that being the first key specified if you also specified fallback keys.
For more information on each method of fallback, please see the fallback documentation.

Namespaces

Namespaces are a feature in i18next internationalization framework which allows you to separate translations that get loaded into multiple files.
While in a smaller project it might be reasonable to just put everything in one file you might get at a point where you want to break translations into multiple files. Reasons might be:
  • You start losing the overview having more than 300 segments in a file
  • Not every translation needs to be loaded on the first page, speed up load time
🎓 Check out this topic in the i18next crash course video.

semantic reasons

Often you wish to separate some segments out because they belong together. We do this in most of our projects, eg.:
  • common.json -> Things that are reused everywhere, eg. Button labels 'save', 'cancel'
  • validation.json -> All validation texts
  • glossary.json -> Words we want to be reused consistently inside texts

technical / editorial reasons

More often you don't want to load all the translations upfront or at least reduce the amount loaded. This reason often goes hand in hand with the one translation file gets too large and you start losing the overview scrolling through hundreds of text fragments.
  • namespace per view/page
  • namespace per application section / feature set (admin area, ...)
  • namespace per module which gets lazy loaded (single page applications)

Sample

i18next.init({
ns: ['common', 'moduleA', 'moduleB'],
defaultNS: 'moduleA'
}, (err, t) => {
i18next.t('myKey'); // key in moduleA namespace (defined default)
i18next.t('common:myKey'); // key in common namespace (not recommended with ns prefix when used in combination with natural language keys)
// better use the ns option:
i18next.t('myKey', { ns: 'common' });
});
// load additional namespaces after initialization
i18next.loadNamespaces('anotherNamespace', (err, t) => { /* ... */ });
Check the extended sample on the getting started page for a running sample.

Fallback

Doing graceful fallbacks is a core principle of i18next. This enables you to display the most accurate content possible, while not repeating content over and over.

Language fallback

Variant resolving - fallback from dialects or scripts

By default, if a variant (containing region, script, etc) is not found, i18next will look for the same key in the broader version of that language. With this in mind, a common strategy if you're supporting language variants is to write common text inside the pure language, specifying only what differs in the variants.
Example:
// fallback to one language
i18next.init({
lng: "en-GB",
resources: {
"en-GB": {
"translation": {
"i18n": "Internationalisation"
}
},
"en": {
"translation": {
"i18n": "Internationalization",
"i18n_short": "i18n"
}
}
}
}, () => {
i18next.t('i18n'); // -> finds "Internationalisation"
i18next.t('i18n_short'); // -> falls back to "en": "i18n"
// force using en
i18next.t('i18n', { lng: 'en' }); // -> "Internationalization"
});

Fallback to different languages

If you can not provide the preferred language for a user, you can specify another language as fallback. This is useful to indicate the main language or, for instance, if you want to keep the fallbacks different per region.
// fallback to one language
i18next.init({
fallbackLng: 'en'
});
// fallback ordered
i18next.init({
fallbackLng: ['fr', 'en']
});
// fallback depending on user language
i18next.init({
fallbackLng: {
'de-CH': ['fr', 'it'], //French and Italian are also spoken in Switzerland
'zh-Hant': ['zh-Hans', 'en'],
'es': ['fr'],
'default': ['en']
}
});
// function that returns an array of fallbacks
// your function may also return a string or object as above
i18next.init({
fallbackLng: (code) => {
if (!code || code === 'en') return ['en-US'];
const fallbacks = [code];
// We maintain en-US and en-AU. Some regions will prefer en-AU.
if (code.startsWith('en-') && !['en-US', 'en-AU'].includes(code)) {
if (['en-GB', 'en-NZ', 'en-IR'].includes(code)) fallbacks.push('en-AU');
else fallbacks.push('en-US');
return fallbacks;
}
// add pure lang
const langPart = code.split('-')[0];
if (langPart !== code) fallbacks.push(langPart);
// finally, developer language
fallbacks.push('dev');
return fallbacks;
}
});
The default is set to dev which means developer language. At first this might look strange to set the default to a language, but this enables to set the saveMissing feature to send new keys to that developer specific language. From there your translators can modify the texts to a translation file containing, for instance, proper English, including defined terminology. For production use, just set fallbackLng to an existing language.

Namespace fallback

i18next by default loads its translations from one file named translation. However, you can configure it to load from multiple files, called namespaces.
Besides defining multiple namespaces to load from, you also can set fallback namespaces. Thus, if a key to translate isn't found in the given namespace, it will look it up in the indicated fallbacks.
app.json
{
"title": "i18next"
}
common.json
{
"button": {
"save": "save"
}
}
Sample
i18next.init({
// files to load
ns: ['app', 'common'],
// default namespace (needs no prefix on calling t)
defaultNS: 'app',
// fallback, can be a string or an array of namespaces
fallbackNS: 'common'
}, () => {
i18next.t('title') // -> "i18next"
i18next.t('button.save') // -> "save" (fallback from common)
// without fallbackNS you would have to prefix namespace
// to access keys in that namespace
// and this is not recommended when used in combination with natural language keys
i18next.t('common:button.save') // -> "save"
// better use the ns option:
i18next.t('button.save', { ns: 'common' }) // -> "save"
});

Key fallback

Key not found

If a key does not return a value the key acts as fallback:
i18next.t('notExistingKey'); // -> "notExistingKey"
So you could configure i18next to have the key being the fallback instead of loading a fallback language:
de.json
{
"No one says a key can not be the fallback.": "Niemand sagt ein key kann nicht als Ersatz dienen."
}
i18next.init({
lng: 'de',
// allow keys to be phrases having `:`, `.`
nsSeparator: false,
keySeparator: false,
// do not load a fallback
fallbackLng: false
});
i18next.t('No one says a key can not be the fallback.')
// -> "Niemand sagt ein key kann nicht als Ersatz dienen."
i18next.t('This will be shown if the current loaded translations do not have this.');
// -> "This will be shown if the current loaded translations do not have this."
While this works and might reduce files to load it makes the management of translations a lot harder as you will need to update changes to fallback values in code and JSON files.
Possible - but not recommended.

Missing values for existing keys

In addition to the above, if you want missing values to fallback to the key in cases where the keys (e.g. got extracted by a code parser) exist in your JSON translation file with empty string as value, you also need this setting:
// allow an empty value to count as invalid (by default is true)
returnEmptyString: false

Calling with fallback keys

Calling the t function with an array of keys enables you to translate dynamic keys providing a non specific fallback value.
As a sample think of an error code you get and you like to show a specific warning in some cases:
translation.json
{
"error": {
"unspecific": "Something went wrong.",
"404": "The page was not found."
}
}
Sample
// const error = '404';
i18next.t([`error.${error}`, 'error.unspecific']) // -> "The page was not found"
// const error = '502';
i18next.t([`error.${error}`, 'error.unspecific']) // -> "Something went wrong"

Plugins

i18next comes with a lot of modules to enhance the features available. There are modules to:
import i18next from 'i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import sprintf from 'i18next-sprintf-postprocessor';
i18next
.use(Backend) // or any other backend implementation
.use(LanguageDetector) // or any other implementation
.use(sprintf) // or any other post processor
.init(options);
For usage details please read the documentation of the plugin (readme file in the repository or npm website).
If you're not sure on why you may need such a plugin, have a look at the First setup help page.

Add or Load Translations

There are a few options to load translations to your application instrumented by i18next. The most common approach to this adding a so called backend plugin to i18next. The range of backends is large from loading translations in the browser using xhr/fetch request to loading translations from databases or filesystem in Node.js.

Add on init

You can add the translations on init
import i18next from 'i18next';
i18next
.init({
resources: {
en: {
namespace1: {
key: 'hello from namespace 1'
},
namespace2: {
key: 'hello from namespace 2'
}
},
de: {
namespace1: {
key: 'hallo von namespace 1'
},
namespace2: {
key: 'hallo von namespace 2'
}
}
}
});

Add after init

You can add the translations after init
import i18next from 'i18next';
i18next.init({ resources: {} });
i18next.addResourceBundle('en', 'namespace1', {
key: 'hello from namespace 1'
});
There are more options to adding, removing translations...learn more about resource handling.
Please make sure to at least pass in an empty resources object on init. Else i18next will try to load translations and give you a warning that you are not using a backend.

Combined with a backend plugin

If you want to lazy load some translations via a backend plugin, you may need to use the partialBundledLanguages: true option. This allows some resources (or no resources) to be set on initialization while others can be loaded using a backend connector.
You may also want to set the ns option. To an empty array if you do not want to load any namespaces (also not the default namespace: 'translation') or to an array containing the namespaces to load.
import i18next from 'i18next';
i18next.init({
partialBundledLanguages: true,
ns: [],
resources: {}
});
i18next.addResourceBundle('en', 'namespace1', {
key: 'hello from namespace 1'
});
// or via backend
// i18next.loadNamespaces(['myNamespace1', 'myNamespace2'])
// i18next.loadLanguages(['de', 'fr'])
// etc.

Lazy load in memory translations

i18next-resources-to-backend helps to transform resources to an i18next backend. This means, you can also lazy load translations, for example when using webpack:
import i18next from 'i18next';
import resourcesToBackend from 'i18next-resources-to-backend';
i18next
.use(resourcesToBackend((language, namespace) => import(`./locales/${language}/${namespace}.json`)))
.init({ /* other options */ })

Load using a backend plugin

Each plugin comes with a set of configuration settings like path to load resources from. Those settings are documented on the individual readme file of each repository.
Here is a sample using the i18next-http-backend to load resources from the server.
import i18next from 'i18next';
import Backend from 'i18next-http-backend';
i18next
.use(Backend)
.init({
backend: {
// for all available options read the backend's repository readme file
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
});
Having a combination of defining resources + having a backend will not implicitly take one or the other source as fallback resources. If you need some fallback behaviour you may use the i18next-chained-backend. A short example can be found here. With i18next-chained-backend you can also create some caching behaviour.
🎓 Check out this topic in the i18next crash course video.

Load using a smart backend plugin serving directly from a CDN

Just use the i18next-locize-backend to load resources from the CDN.
import i18next from 'i18next';
import Backend from 'i18next-locize-backend';
i18next
.use(Backend)
.init({
backend: {
projectId: '[PROJECT_ID]',
apiKey: '[API_KEY]',
referenceLng: '[LNG]'
}
});
Here you can find a step by step guide with a React.js app, which will unleash the full power of i18next in combination with locize. See how your developer experience with this localization workflow could look like. There's also the possibility to have an even more focused developer experience, with the help of the auto-machinetranslation workflow and the use of the save missing keys functionality, new keys not only gets added to locize automatically, while developing the app, but are also automatically translated into the target languages using machine translation (like Google Translate).

locize is the perfect translation management tool for your i18next project


Extracting translations

At some point you will come to the question how to get new translation key/values into your namespace (translation) file.

1) Adding new strings manually

While for sure this is the least efficient method for adding new translations, we know a lot of projects are doing this. There is actually nothing wrong with it beside being some extra work developers could avoid.

2) Using an extraction tool

Static extraction tools can read through your code files to automatically find and export translation keys. i18next-scanner, i18next-parser and babel-plugin-i18next-extract are sensible choices to achieve this goal.
Or try translation-check, it shows an overview of your translations in a nice UI. Check which keys are not yet translated.
For more information about extraction tools, see plugin and utils documentation page.

3) Runtime extraction

I18next has a setting to send all keys that it was unable to resolve during runtime using the attached backend.
In case of the http-backend just set saveMissing: true on init:
import i18n from "i18next";
import detector from "i18next-browser-languagedetector";
import backend from "i18next-http-backend";
import { initReactI18next } from "react-i18next";
i18n
.use(detector)
.use(backend)
.use(initReactI18next) // passes i18n down to react-i18next
.init({
fallbackLng: "en", // use en if detected lng is not available
saveMissing: true // send not translated keys to endpoint
});
export default i18n;
Check the options for where missing translation gets sent.
Using node.js and express? You can get that endpoint for free: https://github.com/i18next/i18next-http-middleware#add-routes
This is the most convenient way of working with react-i18next: just develop and run your applications without worrying too much about adding translations to your catalog as those get added automatically.
Wanna have this process on steroids? Just hook up a locize.com localization project using the provided backend and get both the saveMissing and loading of translations automated in a true continuous localization process. Check out this tutorial!

Caching

Browser caching with local storage

With i18next you can configure a cache layer to be used in the browser. It will load and cache resources from localStorage and can be used in combination with the chained backend.
import i18next from "i18next";
import ChainedBackend from "i18next-chained-backend";
import HttpBackend from "i18next-http-backend";
import LocalStorageBackend from "i18next-localstorage-backend";
i18next
.use(ChainedBackend)
.init({
fallbackLng: "en",
// ... your i18next config
backend: {
backends: [
LocalStorageBackend,
HttpBackend
],
backendOptions: [{
expirationTime: 7 * 24 * 60 * 60 * 1000 // 7 days
}, {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}]
}
});

Server side caching with filesystem

With i18next you can configure a cache layer to be used on server side. It will load and cache resources from the filesystem and can be used in combination with the chained backend.
import i18next from "i18next";
import ChainedBackend from "i18next-chained-backend";
import HttpBackend from "i18next-http-backend";
import FsBackend from "i18next-fs-backend";
i18next
.use(ChainedBackend)
.init({
fallbackLng: "en",
// ... your i18next config
backend: {
backends: [
FsBackend,
HttpBackend
],
backendOptions: [{
expirationTime: 7 * 24 * 60 * 60 * 1000, // 7 days
loadPath: './locales_cache/{{lng}}/{{ns}}.json',
addPath: './locales_cache/{{lng}}/{{ns}}.json' // make sure the folders "locales_cache/{{lng}}" exists
}, {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}]
}
});
More information can be found here: i18next-chained-backend i18next-fs-backend i18next-http-backend

React-native caching with AsyncStorage

With i18next you can configure a cache layer to be used on react-native. It will load and cache resources from the AsyncStorage and can be used in combination with the chained backend.
import i18next from "i18next";
import ChainedBackend from "i18next-chained-backend";
import HttpBackend from "i18next-http-backend";
import AsyncStorageBackend from "i18next-async-storage-backend";
i18next
.use(ChainedBackend)
.init({
fallbackLng: "en",
// ... your i18next config
backend: {
backends: [
AsyncStorageBackend,
HttpBackend
],
backendOptions: [{
expirationTime: 7 * 24 * 60 * 60 * 1000 // 7 days
}, {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}]
}
});

Server side Next.js caching with filesystem

Similar to the normal server side caching with filesystem, you can use the same approach in a Next.js app in combination with next-i18next. It will load and cache resources from the filesystem and can be used in combination with the chained backend.
The config file, will probably look similar to this, but for a more complete example have a look at this example by locize.
// next-i18next.config.js
const ChainedBackend = require('i18next-chained-backend')
const FSBackend = require('i18next-fs-backend/cjs')
const HttpBackend = require('i18next-http-backend/cjs')
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
},
backend: {
backends: [
FSBackend,
HttpBackend
],
backendOptions: [{ // make sure public/locales_cache/en and public/locales_cache/de exists
loadPath: './public/locales_cache/{{lng}}/{{ns}}.json',
addPath: './public/locales_cache/{{lng}}/{{ns}}.json',
expirationTime: 7 * 24 * 60 * 60 * 1000 // 7 days
}, {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}]
},
use: [ChainedBackend],
ns: ['common', 'footer', 'second-page'], // the namespaces needs to be listed here, to make sure they got preloaded
serializeConfig: false, // because of the custom use i18next plugin
// debug: true,
}
We suggest not to use multiple backends with the i18next-chained-backend in combination with saveMissing or updateMissing, because it may happen, that the trigger for this is based on stale data.

Backend Fallback

Do you want to define a fallback which uses local translations?

Browser fallback with local / bundled translations

With i18next you can configure a fallback backend to be used in the browser. It will try to load from your primary backend (in this case from your http backend) and if the primary backend is not reachable or does not serve translations, your second backend (in this case local or bundled translations) will be used. This is all possible thanks to the chained backend.
import i18next from "i18next";
import ChainedBackend from "i18next-chained-backend";
import HttpBackend from "i18next-http-backend";
import resourcesToBackend from "i18next-resources-to-backend";
const bundledResources = {
en: {
translations: {
key: 'value'
}
}
};
i18next
.use(ChainedBackend)
.init({
fallbackLng: "en",
// ... your i18next config
backend: {
backends: [
HttpBackend,
resourcesToBackend(bundledResources)
],
backendOptions: [{
loadPath: '/locales/{{lng}}/{{ns}}.json'
}]
}
});

You can also lazy load the in memory translations, i.e. when using webpack

import i18next from "i18next";
import ChainedBackend from "i18next-chained-backend";
import HttpBackend from "i18next-http-backend";
import resourcesToBackend from "i18next-resources-to-backend";
i18next
.use(ChainedBackend)
.init({
fallbackLng: "en",
// ... your i18next config
backend: {
backends: [
HttpBackend,
resourcesToBackend((lng, ns) => import(`./locales/${lng}/${ns}.json`))
],
backendOptions: [{
loadPath: '/locales/{{lng}}/{{ns}}.json'
}]
}
});

Server side fallback with filesystem

On server side you can also use the i18next-fs-backend for example instead of the in memory fallback.
import i18next from "i18next";
import ChainedBackend from "i18next-chained-backend";
import HttpBackend from "i18next-http-backend";
import FsBackend from "i18next-fs-backend";
i18next
.use(ChainedBackend)
.init({
fallbackLng: "en",
// ... your i18next config
backend: {
backends: [
HttpBackend,
FsBackend
],
backendOptions: [{
loadPath: '/locales/{{lng}}/{{ns}}.json'
}, {
loadPath: './locales_cache/{{lng}}/{{ns}}.json'
}]
}
});
More information can be found here: i18next-chained-backend i18next-fs-backend i18next-http-backend
We suggest not to use multiple backends with the i18next-chained-backend in combination with saveMissing or updateMissing, because it may happen, that the trigger for this is based on stale data.

FAQ

Misc

i18next is awesome. How can I support the project?

There are a lot of ways to support us. Make a PR for a feature requested. Improve the documentation. Help others to get started. Spread the word.
Further you could try locize.com - our localization as a service offering. It's like donating to i18next but with additional benefits for you - like saving hours of time translating your project.

Loading issues

I don't see my translations!!!

Try setting debug: true on init and check the console log. There is rather sure a warning for unable to resolve the loadPath or invalid json. Check if the translation files are accessible via browser.

Translation

How to translate the resource files?

For a quick and dirty machine translation you may have a look at this free translator. But in general we suggest to use a smart Translation Management Service like locize to translate your i18next resources.
For professional translations we advice you to work with human translators. Or at least proofread the results coming from machine translations.

How do i know which plural suffix i have to use?

On the plural page there is a tool to get them.
Or try translation-check, it shows an overview of your translations in a nice UI. It shows also the appropriate plural forms.
Or you use a smart translation management system, like locize.

Why are my plural keys not working?

Are you seeing this warning in the development console?
i18next::pluralResolver: Your environment seems not to be Intl API compatible, use an Intl.PluralRules polyfill. Will fallback to the compatibilityJSON v3 format handling.
With v21 we streamlined the suffix with the one used in the Intl API.
In environments where the Intl.PluralRules API is not available (like older Android devices), you may need to polyfill the Intl.PluralRules API. In case it is not available it will fallback to the i18next JSON format v3 plural handling. And if your json is already using the new suffixes, your plural keys will probably not be shown.
tldr;
npm install intl-pluralrules
import 'intl-pluralrules'

How should the language codes be formatted?

Theoretically, you're not bound to any specific language code format, but if you want to make use of all the in built language features, like proper pluralization and correct fallback resolution, we strongly suggest to use the following iso norm (BCP 47 language tag):
lng-(script)-REGION-(extensions) i.e.
  • en, en-US or en-GB
  • zh, zh-HK or zh-Hant-HK
And more information about the format can be found here: https://www.w3.org/International/articles/language-tags/

Process

How do I keep overview over my translation progress?

Try translation-check, it shows an overview of your translations in a nice UI. Check which keys are not yet translated.
If you need more, it might be time to use a translation management tool.

How to handle with changes in e2e tests?

For e2e tests a good tactic is to set language to cimode on init. This will set i18next to always return the key on calling i18next.t. Want to add the namespace to returned value change appendNamespaceToCIMode: true on init.

How to use i18next in serverless environments?

Due to how serverless functions work, you cannot guarantee that a cached version of your data is available. Serverless functions are short-lived, and can shut down at any time, purging any in-memory or filesystem cache. This may be an acceptable trade-off, but sometimes it isn't acceptable.
Because of this we suggest to not use a remote backend and to download the translations and package them with your serverless function.
Read more about this topic, here.

JSON Format

i18next JSON v4

{
"key": "value",
"keyDeep": {
"inner": "value"
},
"keyNesting": "reuse $t(keyDeep.inner)",
"keyInterpolate": "replace this {{value}}",
"keyInterpolateUnescaped": "replace this {{- value}}",
"keyInterpolateWithFormatting": "replace this {{value, format}}",
"keyContext_male": "the male variant",
"keyContext_female": "the female variant",
"keyPluralSimple_one": "the singular",
"keyPluralSimple_other": "the plural",
"keyPluralMultipleEgArabic_zero": "the plural form 0",
"keyPluralMultipleEgArabic_one": "the plural form 1",
"keyPluralMultipleEgArabic_two": "the plural form 2",
"keyPluralMultipleEgArabic_few": "the plural form 3",
"keyPluralMultipleEgArabic_many": "the plural form 4",
"keyPluralMultipleEgArabic_other": "the plural form 5",
"keyWithArrayValue": ["multipe", "things"],
"keyWithObjectValue": { "valueA": "return this with valueB", "valueB": "more text" }
}
These are the defaults. Nesting and Interpolation formats are configurable.
To learn more about the features check the documentation:
The only difference to v3 is the plural suffixes.
You may need to polyfill the Intl.PluralRules API, in case it is not available it will fallback to the i18next JSON format v3 plural handling.
To convert your existing translations to the new v4 format, have a look at i18next-v4-format-converter or this web tool.

i18next JSON v3

enabled by:
i18next.init({
compatibilityJSON: 'v3'
});
formats:
{
"key": "value",
"keyDeep": {
"inner": "value"
},
"keyNesting": "reuse $t(keyDeep.inner)",
"keyInterpolate": "replace this {{value}}",
"keyInterpolateUnescaped": "replace this {{- value}}",
"keyInterpolateWithFormatting": "replace this {{value, format}}",
"keyContext_male": "the male variant",
"keyContext_female": "the female variant",
"keyPluralSimple": "the singular",
"keyPluralSimple_plural": "the plural",
"keyPluralMultipleEgArabic_0": "the plural form 0",
"keyPluralMultipleEgArabic_1": "the plural form 1",
"keyPluralMultipleEgArabic_2": "the plural form 2",
"keyPluralMultipleEgArabic_3": "the plural form 3",
"keyPluralMultipleEgArabic_4": "the plural form 4",
"keyPluralMultipleEgArabic_5": "the plural form 5",
"keyWithArrayValue": ["multipe", "things"],
"keyWithObjectValue": { "valueA": "return this with valueB", "valueB": "more text" }
}
The only difference to v2 is the plural suffixes for languages with multiple plural forms.

i18next JSON v2

enabled by:
i18next.init({
compatibilityJSON: 'v2'
});
formats:
{
"key": "value",
"keyDeep": {
"inner": "value"
},
"keyNesting": "reuse $t(keyDeep.inner)",
"keyInterpolate": "replace this {{value}}",
"keyInterpolateUnescaped": "replace this {{- value}}",
"keyContext_male": "the male variant",
"keyContext_female": "the female variant",
"keyPluralSimple": "the singular",
"keyPluralSimple_plural": "the plural",
"keyPluralMultipleEgArabic_0": "the plural form 0",
"keyPluralMultipleEgArabic_1": "the plural form 1",
"keyPluralMultipleEgArabic_2": "the plural form 2",
"keyPluralMultipleEgArabic_3": "the plural form 3",
"keyPluralMultipleEgArabic_11": "the plural form 4",
"keyPluralMultipleEgArabic_100": "the plural form 5"
}
These are the defaults. Nesting and Interpolation formats are configurable.

i18next JSON v1

enabled by:
i18next.init({
compatibilityJSON: 'v1'
});
formats:
{
"key": "value",
"keyDeep": {
"inner": "value"
},
"keyNesting": "reuse $t(keyDeep.inner)",
"keyInterpolate": "replace this __value__",
"keyInterpolateUnescaped": "replace this __valueHTML__",
"keyContext_male": "the male variant",
"keyContext_female": "the female variant",
"keyPluralSimple": "the singular",
"keyPluralSimple_plural": "the plural",
"keyPluralMultipleEgArabic": "the plural form 0",
"keyPluralMultipleEgArabic_plural_1": "the plural form 1",
"keyPluralMultipleEgArabic_plural_2": "the plural form 2",
"keyPluralMultipleEgArabic_plural_3": "the plural form 3",
"keyPluralMultipleEgArabic_plural_11": "the plural form 4",
"keyPluralMultipleEgArabic_plural_100": "the plural form 5"
}
These are the defaults. Nesting and Interpolation formats are configurable.

Creating own Plugins

i18next comes with a lot of modules to enhance the features available. There are modules to:
  • load resources, eg. via xhr or from filesystem (node.js)
  • cache resources on client, eg. localStorage
  • detect user language by querystring, navigator, cookie, ...
  • post processors to further manipulate values, eg. to add sprintf support
The plugins need to support following APIs:
HINT: You can provide a singleton or a prototype constructor (prefered for supporting multiple instances of i18next).

backend

Backend plugins are used to load data for i18next.
{
type: 'backend',
init: function(services, backendOptions, i18nextOptions) {
/* use services and options */
},
read: function(language, namespace, callback) {
/* return resources */
callback(null, {
key: 'value'
});
/* if method fails/returns an error, call this: */
/* callback(truthyValue, null); */
},
// or new since i18next v22.1.0
// read: function(language, namespace) {
// return new Promise((resolve) => {
// resolve({
// key: 'value'
// })
// })
// },
// optional
readMulti: function(languages, namespaces, callback) {
/* return multiple resources - useful eg. for bundling loading in one xhr request */
callback(null, {
en: {
translations: {
key: 'value'
}
},
de: {
translations: {
key: 'value'
}
}
});
/* if method fails/returns an error, call this: */
/* callback(truthyValue, null); */
},
// or new since i18next-multiload-backend-adapter v2.1.0
// readMulti: function(languages, namespaces) {
// return new Promise((resolve) => {
// resolve({
// en: {
// translations: {
// key: 'value'
// }
// },
// de: {
// translations: {
// key: 'value'
// }
// }
// })
// })
// },
// only used in backends acting as cache layer
save: function(language, namespace, data) {
// store the translations
},
create: function(languages, namespace, key, fallbackValue) {
/* save the missing translation */
}
}
Using readMulti is only supported when using the https://github.com/i18next/i18next-multiload-backend-adapter

languageDetector

Language Detector plugins are used to detect language in user land.
{
type: 'languageDetector',
async: true, // If this is set to true, your detect function receives a callback function that you should call with your language, useful to retrieve your language stored in AsyncStorage for example
init: function(services, detectorOptions, i18nextOptions) { // optional since v22.3.0
/* use services and options */
},
detect: function(callback) { // You'll receive a callback if you passed async true
/* return detected language */
// callback('de'); if you used the async flag
return 'de';
},
// or new since v22.3.0
// detect: async function () { // you can also return a normal Promise
// /* return detected language */
// return 'de';
// // or
// // return Promise.resolve('de')
// },
cacheUserLanguage: function(lng) { // optional since v22.3.0
/* cache language */
}
// or new since v22.3.0, but i18next will not await for it... so it's basically a fire and forget
// cacheUserLanguage: async function(lng) {
// /* await cache language */
// }
}

post processor

Post Processors are used to extend or manipulate the translated values before returning them in t function.
(Post Processors do not need to be prototype functions)
{
type: 'postProcessor',
name: 'nameOfProcessor',
process: function(value, key, options, translator) {
/* return manipulated value */
return value;
}
}

logger

Override the built in console logger.
(loggers do not need to be prototype functions)
{
type: 'logger',
log: function(args) {},
warn: function(args) {},
error: function(args) {}
}

Helpful tips

Make sure to set the plugin type

If you do not set the plugin type, you may get an error like this.
... No [plugin type] was added via i18next.use. Will not load resources.
If you are creating a class for your plugin, you may set the type like in the following example (the following is an example if you are making a backend plugin):
class Backend {
constructor(services, backendOptions, i18nextOptions){
}
// other required methods;
// ie. read, create, etc.
}
Backend.type = "backend";
export default Backend;

Create a private method to initialize your plugin

The constructor of your plugin (if the plugin is of type backend or languageDetector) will be called without arguments if you use the plugin as a class. Using the plugin as a class looks like this:
import i18n from "i18next";
import {
initReactI18next
} from "react-i18next";
import i18nBackend from "my-custom-backend";
i18n
.use(i18nBackend)
.use(initReactI18next)
.init({
backend: {
// custom options
},
// other options
});
While using your plugin in this way, you may want to validate the options passed into the backend property of the .init method. A good way to validate them is to have a private method where you initialize your plugin.
class Backend {
constructor(services, backendOptions = {}, i18nextOptions = {}){
this.init(services, backendOptions, i18nextOptions);
}
init(services, backendOptions, i18nextOptions){
// Validate backendOptions
}
// other required methods;
// ie. read, create, etc.
}
Backend.type = "backend";
export default Backend;

Migration Guide

v21.x.x to v22.0.0

Since this is a major rewrite for TypeScript usage we decided to create a major version. For JavaScript users v22.0.0 is equivalent to 21.10.0

v20.x.x to v21.0.0

One of the biggest breaking changes is regarding suffixing plurals. This change streamlines the suffix with the one used in the Intl API. You may need to polyfill the Intl.PluralRules API, in case it is not available it will fallback to the i18next JSON format v3 plural handling. To enforce old behaviour you can enable compatibilityJSON = 'v3' on i18next init call.
import i18next from 'i18next';
i18next.init({
compatibilityJSON: 'v3'
}, (err, t) => { /* resources are loaded */ });
There is also support for ordinal numbers (referring to the ordering or ranking of things, e.g. "1st", "2nd", "3rd" in English). Learn more about plurals: https://www.i18next.com/translation-function/plurals
To convert your existing translations to the new v4 format, have a look at i18next-v4-format-converter or this web tool.

skipOnVariables = true

By default the skipOnVariables option now is set to true. To enforce old behaviour you can set skipOnVariables = false on i18next init call.
import i18next from 'i18next';
i18next.init({
interpolation: {
skipOnVariables: false
}
}, (err, t) => { /* resources are loaded */ });

natural language detection

i18next now automatically tries to detect natural language keys. This way there is no need to set nsSeparator or keySeparator option to false. In case you want to skip this natural language detection, provide a keySeparator and/or a nsSeparator option.

removed deprecated

The old deprecated whitelist options and functions have been definitively removed.
  • rename option whitelist to supportedLngs
  • rename option nonExplicitWhitelist to nonExplicitSupportedLngs
  • rename function languageUtils.isWhitelisted to languageUtils.isSupportedCode

new resolvedLanguage

There is a new i18next.resolvedLanguage property, that represents the current resolved language. It can be used as primary used language, for example in a language switcher.

defaultNS

If passing the ns option, the defaultNS will, by default, be set to the first ns passed.

v19.x.x to v20.0.0

There should not be any breaking change, but regarding of some misuse of i18next that pop up in last minor releases, we opted for a major version this time. The relevant change behind the scene for this major version was ignoreJSONStructure.

v18.x.x to v19.0.0

Typescript use export default for esm-first approach 1352. No API changes.

v17.x.x to v18.0.0

  • When calling i18next.changeLanguage() both i18next.language and i18next.languages will be set to the new language after calling loadResources -> means when accessing t function meanwhile you will get still the translations for the previous language instead of the fallback.
  • When is this breaking? this does not break any current test - but if you depend on accessing i18next.language or i18next.dir during language change and expect the new language this will break your app.
  • Reasoning: In react-i18next we get in a not ready state for loaded translations while we would prefer just waiting for the new language ready and trigger a rerender then - also a triggered rerender outside of the bound events would end in Suspense...
  • How can I get the language i18next will be set to? i18next.isLanguageChangingTo is set to the language called

v16.x.x to v17.0.0

  • removes named exports in index.js to avoid issues in bundlers

v15.x.x to v16.0.0

  • Build process was updated - no API changes
  • note: dist/es -> dist/esm, dist/commonjs -> dist/cjs (individual files -> one bundled file)

v14.x.x to v15.0.0

  • Build process was updated - no API changes

v13.x.x to v14.0.0

  • Breaking changes in typescript typings for details have a look at this pull request.

v12.x.x to v13.0.0

  • typescript definitions now can directly be used from the i18next module - no longer needed to get them from DefinitelyTyped
  • functions used to return a callback (eg. init, changeLanguage, loadNamespaces) now also return a Promise - while this enables easier handling using async await this also means eg. i18next.init does not return this anylonger and therefore you can't chain calls like i18next.init().on() anylonger.

v11.x.x to v12.0.0

v10.x.x to v11.0.0

v9.x.x to v10.0.0

brings pt, pt-PT, pt-BR plurals in line with, new pt reflects pt-BR and pt-PT gets a special case for plural handling http://www.unicode.org/cldr/charts/26/supplemental/language_plural_rules.html
code
locale
rule
pt-PT
Portugal Portuguese
nplurals=2; plural=(n != 1);
pt_BR
Brazilian Portuguese
plurals=2; plural=(n > 1);
pt
Portuguese
plurals=2; plural=(n > 1);

v8.x.x to v9.0.0

With v9 we removed the compatibility for the v1 API. So the compatibilityAPI: 'v1' flag won't do anything anymore.
You still can append this manually as we do for our old v1 tests - learn more.

v7.x.x to v8.0.0

The nonExplicitWhitelist flag was changed to be used in user detected language too, before it was restricted to defined fallback languages only.
i18next.init({
fallbackLng: 'en',
whitelist: ['de', 'en', 'zh'],
nonExplicitWhitelist: true
});
// eg. `de-AT` will now pass the whitelist check

v6.x.x to v7.0.0

We used to resolve nb-NO, nn-NO to no as language part mainly because there was no way to proper define per local fallbacks. With v7.0.0 we removed that to enable default behaviour also for norwegian language. To get back the old behaviour you can define multiple fallbacks like:
fallbackLng: {
'nb': ['no', 'en'],
'nn': ['no', 'en'],
'default': ['en']
}
Additional starting from 7.0.0 you could use named exports:
import { init, t } from 'i18next';

v5.x.x to v6.0.0

Return namespace in cimode with appendNamespaceToCIMode option (default now will only return key without namespace - independent of call to t function) #863
This change might break your e2e tests if your depending on the cimode (the returned value can now be set to return always only key or ns+key)

v4.x.x to v5.0.0

Nested keys should not be escaped #854
i18next.cloneInstance() calls now init() (before it depended on having called that function with a callback) #860

v3.x.x to v4.0.0

There is only a small change for webpack2 builds which now targets es build with import/export in place to enable treeshaking (module entrypoint in package.json).
Nothing breaking for non webpack2 users.

v2.x.x to v3.0.0

There is one breaking change regarding suffixing plurals in cases a language has multiple plural forms. Eg. arabic suffixes are now 0, 1, 2, 3, 4, 5 instead of 0, 1, 2, 3, 11, 100.
This change streamlines the suffix with the one used in gettext.
To enforce old behaviour you can enable compatibilityJSON = 'v2' on i18next init call.
import i18next from 'i18next';
i18next.init({
compatibilityJSON: 'v2'
}, (err, t) => { /* resources are loaded */ });

v1.11.x to v2.0.0

While v2.0.0 is a full rewrite of the old codebase it should be possible to run in your app without completely rewrite your integration.

Getting started

The new version does not come with backend, cache and user language detection built in. i18next is more a core library that can be extended with modules on demand. This way it can be seamlessly used in browser, node.js or other javascript runtimes.
Modules are available on npm, via bower or on github to download.

browser

import i18next from 'i18next';
import XHR from 'i18next-xhr-backend';
import Cache from 'i18next-localstorage-cache';
import sprintf from 'i18next-sprintf-postprocessor';
import LanguageDetector from 'i18next-browser-languagedetector';
i18next
.use(XHR)
.use(Cache)
.use(LanguageDetector)
.use(sprintf)
.init(options, callback);
hint for jquery user: use additional jquery-i18next

nodejs

var i18next = require('i18next'),
FilesystemBackend = require('i18next-node-fs-backend'),
sprintf = require('i18next-sprintf-postprocessor');
i18next
.use(FilesystemBackend)
.use(sprintf)
.init(options, callback);

nodejs + express

var express = require('express');
i18next = require('i18next'),
FilesystemBackend = require('i18next-node-fs-backend'),
sprintf = require('i18next-sprintf-postprocessor'),
i18nextMiddleware = require('i18next-express-middleware');
i18next
.use(i18nextMiddleware.LanguageDetector)
.use(FilesystemBackend)
.use(sprintf)
.init(options, callback);
var app = express();
app.use(i18nextMiddleware.handle(i18next)); // expose req.t with fixed lng
app.post('/locales/add/:lng/:ns', i18nextMiddleware.missingKeyHandler(i18next)); // serves missing key route for consumers (browser)
app.get('/locales/resources.json', i18nextMiddleware.getResourcesHandler(i18next)); // serves resources for consumers (browser)
app.listen(3000);

Running v2.0.0 with compatibility flags

Version 2.0.0 has a compatibility layer built in which allows to run it like v1.11.x. Keep in mind this layer will be removed in future version.
import i18next from 'i18next';
i18next.init({
compatibilityAPI: 'v1',
compatibilityJSON: 'v1',
// ...old options from v1
}, (err, t) => { /* resources are loaded */ });
option
description
compatibilityAPI
Will convert init and t options and expose old API functions. Will be removed with future update.
compatibilityJSON
Will allow to use JSON files in v1 format. Using old interpolation prefix, suffix and no need for singular suffix [0] for singular in languages with more then just 1 plural form.

Not supported any longer in v2.0.0

  • support for older browsers:
    Beginning with v2 we target only modern browsers (Chrome, Firefox, ... and IE >= 10).
    For IE9 you will need to add a es5 shim
  • jquery:
    use additional jquery-i18next
  • synchronous loading:
    i18next.init({ getAsync: false });
    is not supported any longer - as not encouraged by browsers.
  • no conflict:
    i18n.noConflict();
    was removed as i18next registers to window.i18next and no longer to window.i18n
  • indefinite article:
    i18n.t('myKey', { indefinite_article: true })
    was removed - as the solution was too limited - reconsidering adding a better solution in a future v2 release

The history of i18next

How all began - back in 2011

All started back in 2011 when we were in search for an internationalization library that meets our demand - allowing to run both on server side Node.js and on our client side single page applications.
The first i18next landing page in 2011: http://i18next.github.io/i18next/
I18next was born and fastly grown to one of the most used frameworks for translating web applications and in Node.js. The response of the community was amazing and a fast growing ecosystem established itself around i18next.

V2

With v2 of i18next, released in 2015, we completely rebuild i18next to be as extensible as possible.
Since v1 all newer releases can be dropped in as a replacement for v1 by just adding a minimal compatibility layer. The v1 API is still actively tested, here.
Shortly after the i18next v2 release in 2015, another big community driven i18next extension was released: react-i18next.

Internationalization (i18n) is not enough

Our community provided us with great feedback. Out of that response and our own experiences we learnt providing instrumentation for doing proper internationalization just is not enough.
Helping developers to get their applications translated is great - but there is more to it.
  • How do you integrate any translation services / agencies?
  • How do you keep track of new or removed content?
  • How do you handle proper versioning?
  • How do you deploy translation changes without deploying your complete application?
  • and a lot more...

locize to the rescue

Having created the foundation with i18next it was a long journey to localization as a service.
🙏 The best way to directly support the future of i18next is to use locize. 💙

And finally a fair marketplace...

Recently also a new go-to marketplace for clients and translators to get translation jobs done has been launched.
As client you'll find translators and as translator you'll find translation jobs. Direct contact, no middleman – just localistars working together!
localistars